/*
 * 文件名：HandleAlipayServiceImpl.java
 * 版权：
 * 描述：TODO
 * 修改人：zuo liubo
 * 修改时间：2013-3-15 下午4:59:04
 */
package com.scpii.api.service.alipay.impl;

import java.io.ByteArrayInputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Service;

import com.scpii.api.common.EcConstants;
import com.scpii.api.common.exception.ClientException;
import com.scpii.api.domain.app.AppConfigs;
import com.scpii.api.domain.ec.Order;
import com.scpii.api.domain.ec.PaymentLog;
import com.scpii.api.persistence.app.AppConfigsMapper;
import com.scpii.api.persistence.ec.OrderMapper;
import com.scpii.api.persistence.ec.PaymentLogMapper;
import com.scpii.api.service.alipay.HandleAlipayService;
import com.scpii.api.service.alipay.base.AlipayConstants;
import com.scpii.api.service.alipay.base.NotifyRes;
import com.scpii.api.service.alipay.util.AlipayUtil;
import com.scpii.api.service.alipay.util.MD5Signature;
import com.scpii.api.service.alipay.util.ParameterUtil;
import com.scpii.api.service.alipay.util.XMapUtil;
import com.scpii.api.service.ec.PayService;

/**
 * @description 主要处理支付的通知和回调
 * @author zuo liubo
 * @date 2013-3-15
 */
@Service("handleAlipayService")
public class HandleAlipayServiceImpl implements HandleAlipayService {

	
	@Resource
	private PaymentLogMapper paymentLogMapper;
	@Resource
	private OrderMapper orderMapper;
	@Resource
	private PayService payService;
	@Resource
	private AppConfigsMapper appConfigsMapper;

	private static Log logger = LogFactory.getLog(HandleAlipayServiceImpl.class);
	@Override
	public Object callbackFromAlipay(HttpServletRequest request) {
		// 获得通知签名
		String sign = request.getParameter("sign");
		String result = request.getParameter("result");
		String requestToken = request.getParameter("request_token");
		String outTradeNo = request.getParameter("out_trade_no");
		String tradeNo = request.getParameter("trade_no");
		Map<String, String> resMap = new HashMap<String, String>();
		resMap.put("result", result);
		resMap.put("request_token", requestToken);
		resMap.put("out_trade_no", outTradeNo);
		resMap.put("trade_no", tradeNo);

		String verifyData = ParameterUtil.getSignData(resMap);
		logger.info("callback verifyData:" + verifyData);
		boolean verified = false;
		// 使用MD5验签名
		try {
			String orderId = AlipayUtil.getOrderIdFromOrderSn(outTradeNo);
			Order order = getOrderByOrderId(orderId);
			List<AppConfigs> _appConfigs = appConfigsMapper.selectByType(order.getAppId(), AppConfigs.Type.ALIPAY);
			Map<String, String> appConfigs = AlipayUtil.appConfigs2Map(_appConfigs);
			String key = appConfigs.get(AppConfigs.ALIPAY_KEYWORD.PRIVATE_KEY);
			verified = MD5Signature.verify(verifyData, sign, key);

			if (!verified || !result.equals("success")) {
				// TODO
				logger.info("error");
			} else {
				// TODO
				logger.info("success");
			}

		} catch (Exception e) {
			throw new ClientException("校验失败! 订单ID为:" + outTradeNo);
		}
		return null;
	}

	@Override
	public Boolean notifyFromAlipay(HttpServletRequest request) {
		try {
			// 获得通知参数
			@SuppressWarnings("rawtypes")
			Map map = request.getParameterMap();
			// 获得通知签名
			String sign = (String) ((Object[]) map.get("sign"))[0];
			// 获得待验签名的数据
			String verifyData = getVerifyData(map);
			logger.info("notify verifyData:" + verifyData);

			//解析通知返回数据
			String notifyData = (String) ((Object[]) map.get("notify_data"))[0];
			XMapUtil.register(NotifyRes.class);
			NotifyRes notifyRes = (NotifyRes) XMapUtil.load(new ByteArrayInputStream(notifyData.getBytes("UTF-8")));
			
			logger.info("接收到通知   notifyId=" + notifyRes.getNotifyId()); 
			// 1、验签名
			String orderSn = notifyRes.getOrderSn();
			String orderId = AlipayUtil.getOrderIdFromOrderSn(orderSn);
			Order o = getOrderByOrderId(orderId);
			List<AppConfigs> _appConfigs = appConfigsMapper.selectByType(o.getAppId(), AppConfigs.Type.ALIPAY);
			Map<String, String> appConfigs = AlipayUtil.appConfigs2Map(_appConfigs);
			String key = appConfigs.get(AppConfigs.ALIPAY_KEYWORD.PRIVATE_KEY);
			logger.info("notify   verifyData=" + verifyData);
			logger.info("notify   sign=" + sign);
			logger.info("notify   key=" + key);
			if (MD5Signature.verify(verifyData, sign, key)) {
				String tradeStatus = notifyRes.getTradeStatus();
				String notifyId = notifyRes.getNotifyId();
				
				if(isExistNotifyId(notifyId)){
					return true;
				}
				
				Order order = new Order();
				order.setOrderId(Long.valueOf(orderId));
				List<Order> orderList = orderMapper.selectOrderBySelective(order);
				if(orderList.size() != 1){
					return false;
				}
				order = orderList.get(0);
				
				PaymentLog paymentLog = new PaymentLog();
				paymentLog.setOrderSn(orderSn);
				paymentLog.setUserId(order.getUserId());
				paymentLog.setAppId(Long.parseLong(order.getAppId()+""));
				paymentLog.setOrderId(Long.valueOf(orderId));
				paymentLog.setPayAmount(Float.parseFloat(notifyRes.getTotalFee()));
				paymentLog.setPay3tdMethod("即时到账");
				paymentLog.setPay3tdResponseId(notifyRes.getTradeNo());
				paymentLog.setPaymentId(Long.parseLong(AppConfigs.Type.ALIPAY+""));
				paymentLog.setPay3tdUserName(notifyRes.getBuyerId());
				paymentLog.setPay3tdBizUsername(notifyRes.getSellerId());
				paymentLog.setPay3tdStatus(tradeStatus);
				paymentLog.setNotifyId(notifyId);
				paymentLog.setQuantity(notifyRes.getQuantity());
				paymentLog.setPay3tdUserEmail(notifyRes.getBuyerEmail());
				paymentLog.setPay3tdBizEmail(notifyRes.getSellerEmail());
				paymentLog.setLogType(AlipayConstants.paymentLogType.PAY_LOG_ALIPAY_NOTIFY);
				paymentLog.setCreateDt(new Date());
				paymentLog.setTimeOfPayment(notifyRes.getGmtPayment());
				paymentLog.setNotifyData(notifyData);
				
				// 2、根据交易状态处理业务逻辑
				// 交易成功
				if ("TRADE_FINISHED".equals(tradeStatus)) {
					order.setPayStatus(EcConstants.PayStatus.PAYMENT_SUCCESS);
					order.setUpdateDt(new Date());
					paymentLog.setPayStatus(EcConstants.PayStatus.PAYMENT_SUCCESS);
				}
				// 支付成功
				else if ("TRADE_SUCCESS".equals(tradeStatus)) {
					
				}
				//等待卖家支付
				else if ("WAIT_BUYER_PAY".equals(tradeStatus)) {
					
				}
				// 交易关闭
				else if ("TRADE_CLOSE".equals(tradeStatus)) {
					order.setPayStatus(EcConstants.OrderStatus.CANCELED);
					order.setUpdateDt(new Date());
					paymentLog.setPayStatus(EcConstants.PayStatus.PAYMENT_ING);
				}

				//	3、更新订单信息
				orderMapper.updateByPrimaryKeySelective(order);
				
				// 4、写支付日志记录
				paymentLogMapper.insert(paymentLog);
				
				// 5、更新产品信息（ 赠送积分）
				payService.updateGiveUserPoints(order);
				
				// 6、当交易状态成功，处理业务逻辑成功。回写success
				return true;
			} else {
				logger.info("接收支付宝系统通知验证签名失败，请检查！");
				logger.info("签名校验认证  失败");
			}
		} catch (Exception e) {
			logger.error(e.getMessage());
		}
		return false;
	}
	
	

	/**
	 * 判断该通知ID是否存在,存在表示已经处理过了该信息,不存在表示还没有处理该信息
	 * @param notifyId	通知ID
	 * @return true表示已经处理了该ID,false表示还没有处理该通知ID
	 */
	private boolean isExistNotifyId(String notifyId){
		PaymentLog paymentLog = new PaymentLog();
		paymentLog.setNotifyId(notifyId);
		paymentLog.setLogType(AlipayConstants.paymentLogType.PAY_LOG_ALIPAY_NOTIFY);
		// 1、判断该通知是否已经处理过，防止重复处理
		List<PaymentLog> payList = paymentLogMapper.selectLogByNotifyId(paymentLog);
		if(payList.size() > 0 ){
			return true;
		}
		return false;
	}
	
	/**
	 * 获得验签名的数据
	 * 
	 * @param map
	 * @return
	 * @throws Exception
	 */
	private String getVerifyData(Map map) {
		String service = (String) ((Object[]) map.get("service"))[0];
		String v = (String) ((Object[]) map.get("v"))[0];
		String sec_id = (String) ((Object[]) map.get("sec_id"))[0];
		String notify_data = (String) ((Object[]) map.get("notify_data"))[0];
		logger.info("通知参数为：" + "service=" + service + "&v=" + v + "&sec_id=" + sec_id + "&notify_data=" + notify_data);
		return "service=" + service + "&v=" + v + "&sec_id=" + sec_id + "&notify_data=" + notify_data;
	}
	
	
	private Order getOrderByOrderId(String orderId){
		Order o = new Order();
		o.setOrderId(Long.parseLong(orderId));
		List<Order> orders = orderMapper.selectOrderBySelective(o);
		if(orders.size() == 1){
				return orders.get(0);
		}
		return null;
	}
}
